package com.miwzn.boot.component.aspect;

import com.alibaba.fastjson.JSON;
import com.google.gson.JsonObject;
import com.miwzn.boot.component.config.shiro.ShiroUser;
import com.miwzn.boot.modules.sys.entity.po.SysLog;
import com.miwzn.boot.modules.sys.service.SysLogService;
import com.miwzn.comm.annotation.OperationLog;
import com.miwzn.comm.constant.LogType;
import com.miwzn.comm.utils.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Auther: wzn
 * @Date: 2019/1/10 17:08
 * @Description:
 */
@Aspect  // 使用@Aspect注解声明一个切面
@Component
public class SysLogAspect {
    private static Logger logger = LoggerFactory.getLogger(SysLogAspect.class);

    @Autowired
    private SysLogService sysLogService;

    @Pointcut("@annotation(com.miwzn.comm.annotation.OperationLog)")
    public void logPointCut() {
    }

    /**
     * 环绕通知 @Around  ， 当然也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param point
     *
     * @return
     *
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time);
        } catch (Exception e) {
        }
        return result;
    }

    /**
     * 保存日志
     *
     * @param joinPoint
     * @param time
     */
    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            SysLog log = new SysLog();
            log.setTime(time);
            OperationLog operationLog = method.getAnnotation(OperationLog.class);
            if (operationLog != null) {
                //注解上的描述
                log.setOperation(operationLog.operation());
                log.setDescription(operationLog.content());
                log.setType(operationLog.type().name());
            }
            //请求的 类名、方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = signature.getName();
            log.setMethod(className + "." + methodName);

            //获取request
            HttpServletRequest request = RequestUtils.getRequest();
            //设置IP地址
            log.setIp(IPUtils.getIp(request));
            //请求的参数
            Object[] args = joinPoint.getArgs();

            if (args != null && args.length > 0) {
                if (args[0] instanceof Throwable) {
                    Throwable e = (Throwable) args[0];
                    log.setOperation(LogType.ERROR.name());
                    log.setParams(ExceptionsUtils.getStackTraceAsString(e));
                } else {
                    String params = JSONUtils.obj2json(Arrays.asList(args));
                    log.setParams(JSON.parse(params).toString());
                }
            }
            try {
                //用户名
                ShiroUser currentUser = CommUtil.getLoginUser();
                String username = currentUser.getUsername();
                log.setUsername(username);
            } catch (Exception e) {
            }
            if (logger.isDebugEnabled()) {
                logger.debug("===========保存日志：{},", JSONUtils.obj2json(log));
            }
            sysLogService.insert(log);
        } catch (Exception e) {
            logger.error("保存日志失败", e);
        }
    }


    /**
     * 异常通知方法只在连接点方法出现异常后才会执行，否则不执行。在异常通知方法中可以获取连接点方法出现的异常。在切面类中异常通知方法
     * 通过throwing属性指定连接点方法出现异常信息存储在ex变量中，在异常通知方法中就可以从ex变量中获取异常信息了
     *
     * @param point
     * @param e
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint point, Throwable e) {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        logger.info("===========afterThrowing连接点方法为：{},参数为：{},异常为：{}", methodName, args, e);
    }
}
